﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using DeVetma.admin.models;
using DeVetma.admin.classes;
using DeVetma.admin.models.database;
using DeVetma.admin.models.database.metadata;
using DeVetma.admin.models.managers;

namespace DeVetma.admin.models.entities
{
    [Entity(table = "Order")]
    public class Order
    {
        [EntityField(column = "oID", keyField = true, nullValue = null)]
        public string oID { get; set; }

        [EntityField(column = "oDate", keyField = false, nullValue = null)]
        public DateTime oDate { get; set; }

        [EntityField(column = "oStatus", keyField = false, nullValue = -1)]
        public int oStatus { get; set; }

        [EntityField(column = "mUsername", keyField = false, nullValue = null)]
        public string mUsername { get; set; }

        public enum OrderStatus { Cart = 0, Pending = 1, Confirmed = 2, Completed = 3,
            Cancelled = 4, Expired = 5, Deleted = 6 }

        public bool isCart { get { return oStatus == (int)OrderStatus.Cart; } }
        public bool isPending { get { return oStatus == (int)OrderStatus.Pending; } }
        public bool isConfirmed { get { return oStatus == (int)OrderStatus.Confirmed; } }
        public bool isCompleted { get { return oStatus == (int)OrderStatus.Completed; } }
        public bool isCancelled { get { return oStatus == (int)OrderStatus.Cancelled; } }
        public bool isExpired { get { return oStatus == (int)OrderStatus.Expired; } }
        public bool isDeleted { get { return oStatus == (int)OrderStatus.Deleted; } }

        public Member member
        {
            get { return getMember(); }
            set { this.mUsername = value.mUsername; }
        }

        public decimal totalOrderCost
        {
            get { return calculateOrderCost(); }
        }

        public int totalOrderItems
        {
            get { return orderDetails.Count; }
        }

        public bool hasOrderItems
        {
            get { return totalOrderItems > 0; }
        }

        public List<OrderDetails> orderDetails
        {
            get { return getOrderDetails(); }
            set { setOrderDetails(value); }
        }

        public List<Clothes> orderedClothes
        {
            get { return getOrderedClothes(); }
        }

        private Member getMember()
        {
            return new MemberManager().get(this.mUsername);
        }

        private List<OrderDetails> getOrderDetails()
        {
            return new OrderDetailsManager().getByOrderId(this.oID);
        }

        private void setOrderDetails(List<OrderDetails> ods)
        {
            OrderDetails[] current = getOrderDetails().ToArray();
            OrderDetails[] toDelete = current.Except(ods.ToArray()).ensure().ToArray();
            OrderDetails[] toAdd = ods.ToArray().Except(current).ensure().ToArray();

            OrderDetailsManager odm = new OrderDetailsManager();
            odm.deleteAll(toDelete, null);
            odm.addAll(toAdd, null);
        }

        private decimal calculateOrderCost()
        {
            OrderDetails[] ods = orderDetails.ToArray();
            decimal orderCost = 0.0M;

            foreach (var od in ods)
                orderCost += od.subtotal;

            return orderCost;
        }

        private List<Clothes> getOrderedClothes()
        {
            return null;
        }

        public static Order cart(string mUsername, Dictionary<string,int> orderItems)
        {
            OrderManager om = new OrderManager();

            Order odr = new Order
            {
                mUsername = mUsername,
                oDate = DateTime.Now,
                oStatus = (int)OrderStatus.Cart
            };

            if (!om.hasCartAlready(mUsername) && om.add(odr))
                return odr;

            return null;
        }

        public bool addOrderDetails(string cID, int qty)
        {
            if (qty < 1)
                return false;

            OrderDetailsManager odm = new OrderDetailsManager();
            OrderDetails existing = odm.getByOrderClothes(this.oID, cID);

            if (existing != null)
            {
                existing.odQuantity += qty;
                return odm.update(existing);
            }

            existing = new OrderDetails
            {
                oID = this.oID,
                cID = cID,
                odQuantity = qty,
                odPrice = new ClothesManager().get(cID).cSelling
            };

            return odm.add(existing);
        }

        public bool updateOrderDetails(string cltID, int qty, 
            decimal price = -1M, string odID = null)
        {
            if (qty < 1 || (cltID == null && odID == null))
                return false;

            OrderDetailsManager odm = new OrderDetailsManager();
            OrderDetails existing = (cltID == null ? odm.get(odID) : 
                odm.getByOrderClothes(this.oID, cltID));

            if (existing == null)
                return false;

            existing.odQuantity = qty;
            if (price > 0.0M)
                existing.odPrice = price;

            return odm.update(existing);
        }

        public bool deleteOrderDetails(string cltId, string odID = null)
        {
            if (cltId == null && odID == null)
                return false;

            OrderDetailsManager odm = new OrderDetailsManager();
            OrderDetails existing = (cltId == null ? new OrderDetails { odID = odID } :
                odm.getByOrderClothes(this.oID, cltId));

            return odm.delete(existing);
        }

        public bool clearAllOrderDetails()
        {
            OrderDetailsManager odm = new OrderDetailsManager();

            return odm.deleteAll(orderDetails.ToArray(), null);
        }

        public bool checkout()
        {
            if (this.oStatus == (int)OrderStatus.Cart)
            {
                this.oDate = DateTime.Now;
                this.oStatus = (int)OrderStatus.Pending;

                return true;
            }

            return false;
        }

        public bool confirm()
        {
            if (this.oStatus == (int)OrderStatus.Pending)
            {
                this.oStatus = (int)OrderStatus.Confirmed;
                return true;
            }

            return false;
        }

        public bool complete()
        {
            if (this.oStatus == (int)OrderStatus.Confirmed)
            {
                this.oStatus = (int)OrderStatus.Completed;

                return true;
            }

            return false;
        }

        public bool expire()
        {
            if (this.oStatus == (int)OrderStatus.Cart)
            {
                this.oStatus = (int)OrderStatus.Expired;
                
                List<OrderDetails> ods = orderDetails;
                foreach (var od in ods)
                    od.clothe.addStock(od.odQuantity);

                return true;
            }

            return false;
        }

        public bool cancel()
        {
            if (this.oStatus < (int)OrderStatus.Confirmed)
            {
                this.oStatus = (int)OrderStatus.Cancelled;

                List<OrderDetails> ods = orderDetails;

                foreach (var od in ods)
                    od.clothe.addStock(od.odQuantity);

                return true;
            }

            return false;
        }

        public bool delete()
        {
            // orders that have been completed and/or confirmed will not be able
            // to be deleted.
            if (this.oStatus != (int)OrderStatus.Completed && this.oStatus != 
                (int)OrderStatus.Confirmed)
            {
                this.oStatus = (int)OrderStatus.Deleted;
                
                List<OrderDetails> ods = orderDetails;
                foreach (var od in ods)
                    od.clothe.addStock(od.odQuantity);

                return true;
            }

            return false;
        }

        public override bool Equals(object obj)
        {
            if (obj is Order && ((Order)obj).oID == oID)
                return true;

            return false;
        }
    }
}